﻿#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_system.h"

#include "rom/ets_sys.h"

#include <errno.h>
#include "esp_err.h"
#include "esp_log.h"

#include "driver/gpio.h"
#include "soc/gpio_num.h"


#include "myiic.h"

#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
#endif

#define GPIO_IIC_SCL          (gpio_num_t)39         /*!< gpio number for I2C master clock */
#define GPIO_IIC_SDA          (gpio_num_t)40         /*!< gpio number for I2C master data  */
#define GPIO_IIC_PIN_SEL	  (1ULL<<GPIO_IIC_SCL|1ULL<<GPIO_IIC_SDA)


//I0方向设置
#define SDA_IN()	gpio_set_direction(GPIO_IIC_SDA,GPIO_MODE_INPUT)
#define SDA_OUT()   gpio_set_direction(GPIO_IIC_SDA,GPIO_MODE_OUTPUT)
#define SDA(value)	(value)? gpio_set_level(GPIO_IIC_SDA,1):gpio_set_level(GPIO_IIC_SDA,0)
#define READ_SDA()	gpio_get_level(GPIO_IIC_SDA)//输入SDA


#define SCL(value)	(value)? gpio_set_level(GPIO_IIC_SCL,1):gpio_set_level(GPIO_IIC_SCL,0)

typedef enum {
	MY_I2C_MASTER_WRITE = 0,
	MY_I2C_MASTER_READ,
}my_i2c_rw_t;
/*!< I2C write data *//*!< I2C read data */

 SemaphoreHandle_t my_ii_mutex=NULL; 
 
#define MY_I2C_MS_TO_WAIT 200
#define MY_I2C_TICKS_TO_WAIT (200/portTICK_PERIOD_MS)
#define MY_I2C_MUTEX_TICKS_TO_WAIT (200/portTICK_PERIOD_MS)

 #define MY_I2C_CHECK(a, str, ret) if(!(a)) { \
        ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
        return (ret); \
    }

#define MY_I2C_CHECK_GOTO(a, str, label) if(!(a)) { \
        ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
        goto label; \
    }

#define MY_I2C_INIT_CHECK(is_init, ret) if(!is_init) { \
        ESP_LOGE(TAG,"%s:%d (%s):i2c_bus has not inited", __FILE__, __LINE__, __FUNCTION__); \
        return (ret); \
    }

#define MY_I2C_MUTEX_TAKE(mutex, ret) if (!xSemaphoreTake(mutex, MY_I2C_MUTEX_TICKS_TO_WAIT)) { \
        ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", MY_I2C_MUTEX_TICKS_TO_WAIT); \
        return (ret); \
    }

#define MY_I2C_MUTEX_TAKE_MAX_DELAY(mutex, ret) if (!xSemaphoreTake(mutex, portMAX_DELAY)) { \
        ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", portMAX_DELAY); \
        return (ret); \
    }

#define MY_I2C_MUTEX_GIVE(mutex, ret) if (!xSemaphoreGive(mutex)) { \
        ESP_LOGE(TAG, "i2c_bus give mutex failed"); \
        return (ret); \
    }
 
 
 
//ets_delay_us(10); 10us对应的是100k
//ets_delay_us(5);   5us对应的是200k
//ets_delay_us(3); 2.5μs对应的是400k

//起始信号:
/*****
 * @Edit 2023.7
 * @brief 产生IIC起始信号
 * @param[in] Null
 * @return Null
******/
static void iic_start(void)
{
	SDA_OUT();     //sda线输出
	//SCL(0);
	//ets_delay_us(10);
	SDA(1);
	//ets_delay_us(10);	
	SCL(1);
	ets_delay_us(10);// 确保总线稳定
 	SDA(0);//START:when CLK is high,DATA change form high to low  在SDA和SCL高电平的同时，将SDA拉低产生一个下降沿
	ets_delay_us(10);// 确保起始信号持续时间足够
	SCL(0);// 拉低SCL线，完成起始信号的发送  可以不在这里拉低 在send byte的地方也是可以的//为了SCL低电平期间sdo的输出变化的电平 因为SCL上升沿不允许sdo电平变化
	ets_delay_us(10);// 确保总线稳定
}	  
//停止信号
/*****
 * @Edit 2023.7
 * @brief 产生IIC停止信号
 * @param[in] Null
 * @return Null
******/
static void iic_stop(void)
{
	SDA_OUT();//sda线输出
	SCL(0);
	//ets_delay_us(10);
	SDA(0);//STOP:when CLK is high DATA change form low to high
 	ets_delay_us(10);	// 确保信号稳定
	SCL(1); 				// 拉高SCL线
	ets_delay_us(10);	// 确保SCL高电平期间SDA的变化可以被正确识别
	SDA(1);					// 在SCL为高电平时，拉高SDA线，生成停止条件
	ets_delay_us(10);	// 确保停止条件被正确发送						   	
}
//等待应答
/*****
 * @Edit 2023.7
 * @brief 等待应答信号到来
 * @param[in] Null
 * @return 0,接收应答成功
 *         1,接收应答失败
******/
static uint8_t iic_wait_ack(void)
{//IIC在开始信号发出后开始发送数据，数据以8位传输，SCL高电平的时候SDA读到的数据有效，然后经历8位数据传输以后，第九次检测应答信号，如果检测到从机将SDA置为低电平，说明从机设备有应答（ACK），如果保持高电平，说明从机设备没有应答（NACK
	uint8_t ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA(1); ets_delay_us(10);	   
	SCL(1); ets_delay_us(10);	 
	while(READ_SDA())
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			iic_stop();//没有应答停止
			return 1;
		}
	}
	SCL(0);//时钟输出0 	   
	return 0;  
} 
//产生应答
/*****
 * @Edit 2023.7
 * @brief 产生ACK应答
 * @param[in] Null
 * @return Null
******/
static void iic_ack(void)
{
	SCL(0);					
	SDA_OUT();
	SDA(0);					// 拉低SDA线以生成ACK信号 
	ets_delay_us(10);   // 确保信号稳定
	SCL(1);					// 拉高SCL线，通知从设备ACK信号已发送
	ets_delay_us(10);	// 确保从设备可以检测到ACK信号
	SCL(0);
}
//不产生应答	 
/*****
 * @Edit 2023.7
 * @brief 不产生ACK应答
 * @param[in] Null
 * @return Null
******/   
static void iic_nack(void)
{
	SCL(0);
	SDA_OUT();
	SDA(1);
	ets_delay_us(10);
	SCL(1);
	ets_delay_us(10);
	SCL(0);
}	
//写一个字节
/*****
 * @Edit 2023.7
 * @brief IIC发送一个字节
 * @param[in] txd:要发送的一个字节
 * @return Null
******/  
static void iic_send_byte(uint8_t txd)
{                        
    uint8_t t;   
	SDA_OUT(); 	  //要输出数据了 sdo改为输出状态  
    SCL(0);//拉低时钟准备数据
    for(t=0;t<8;t++)
    {              
		SDA((txd&0x80)>>7);//数据只能在SCL低电平期间变化 高位bit先发送
		txd<<=1; 	  
		ets_delay_us(10);  	//延时一下是为了让sdo的电平稳定 
		SCL(1);					//上升沿 从机锁住sdo的电平状态
		ets_delay_us(10); 
		SCL(0);	
		ets_delay_us(10);
    }	 
}

//读一个字节
/*****
 * @Edit 2023.7
 * @brief 读1个字节，ack=1时，发送ACK，ack=0，发送nACK   
 * @param[in] ack:0，不发送ACK；1，发送ACK
 * @return 1个字节
******/  
static uint8_t iic_read_byte(void)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        SCL(0);	
        ets_delay_us(10);
		SCL(1);
		
        receive<<=1;
        
        if(READ_SDA())
        {
			receive++; 	
		}
 
		ets_delay_us(10); 
    }					 
//    if (!ack)
//        iic_nack();//发送nACK
//    else
//        iic_ack(); //发送ACK   
        
    return receive;
}




//读1个字节
esp_err_t my_iic_read_byte(uint8_t dev_addr,uint8_t mem_address, uint8_t *data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		if(mem_address!=0xff)
		{
			iic_start();
			//发送器件地址,写数据
			iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
			iic_wait_ack();//应答
			//发送读寄存器地址
			iic_send_byte(mem_address);
			iic_wait_ack();//应答
		}
		
		iic_start();
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_READ);//进入接收模式
		iic_wait_ack();//应答
	    // Read data bytes

		*data = iic_read_byte();//读数据
		iic_ack(); //发送ACK 

		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}

esp_err_t my_iic_read_byte1(uint8_t dev_addr, uint8_t *data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		//发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
		iic_wait_ack();//应答

		*data = iic_read_byte();//读数据
		iic_ack(); //发送ACK 

		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}
//读n个字节
esp_err_t my_iic_read_bytes(uint8_t dev_addr,uint8_t mem_address, size_t data_len, uint8_t *data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		// Send device address with write bit (0) //发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
		iic_wait_ack();//应答
		//发送读寄存器地址
		iic_send_byte(mem_address);
		iic_wait_ack();//应答
		// Send device address with read bit (1)
		iic_start();
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_READ);//进入接收模式
		iic_wait_ack();//应答
	    for(size_t i = 0; i < data_len; i++) 
	    {			
			data[i] = iic_read_byte();//读数据
			iic_ack(); //发送ACK 
		}	
		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}
esp_err_t my_iic_read_nbytes(uint8_t dev_addr, size_t data_len, uint8_t *data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		// Send device address with write bit (0) //发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_READ);
		iic_wait_ack();//应答

	    for(size_t i = 0; i < data_len; i++) 
	    {			
			data[i] = iic_read_byte();//读数据
			iic_ack(); //发送ACK 
		}	
		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}
//写1个字节
esp_err_t my_iic_write_byte1(uint8_t dev_addr, uint8_t data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		 // Send device address with write bit (0) //发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
		iic_wait_ack();//应答
		//发送读寄存器地址
				
		iic_send_byte(data);//发送数据
		iic_wait_ack();//应答
	
		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}
esp_err_t my_iic_write_byte(uint8_t dev_addr,uint8_t mem_address, uint8_t data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		 // Send device address with write bit (0) //发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
		iic_wait_ack();//应答
		//发送读寄存器地址
		iic_send_byte(mem_address);
		iic_wait_ack();//应答
				
		iic_send_byte(data);//发送数据
		iic_wait_ack();//应答
	
		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}
//写n个字节
esp_err_t my_iic_write_bytes(uint8_t dev_addr,uint8_t mem_address, size_t data_len, const uint8_t *data)
{
	if(pdTRUE == xSemaphoreTake(my_ii_mutex, 500/portTICK_PERIOD_MS))
	{  //起始信号
		iic_start();
		//发送器件地址,写数据
		iic_send_byte((dev_addr << 1) | MY_I2C_MASTER_WRITE);
		iic_wait_ack();//应答
		//发送读寄存器地址
		iic_send_byte(mem_address);
		iic_wait_ack();//应答
		
	    for(size_t i = 0; i < data_len; i++) 
	    {			
			iic_send_byte(data[i]),//发送数据 
			iic_wait_ack();//应答
		}	
		//停止信号
		iic_stop(); 
		
		xSemaphoreGive(my_ii_mutex);
	 
	 	return ESP_OK; 	
	}

 	return ESP_FAIL; 
}

esp_err_t my_iic_init(void)
{  
	if(!my_ii_mutex)
	{
		my_ii_mutex= xSemaphoreCreateMutex();
		if(!my_ii_mutex)
		{
			return ESP_FAIL;			
		}
		
		gpio_pad_select_gpio(GPIO_IIC_SCL);
		gpio_set_direction(GPIO_IIC_SCL, GPIO_MODE_OUTPUT);
		
		gpio_pad_select_gpio(GPIO_IIC_SDA);
		gpio_set_direction(GPIO_IIC_SDA, GPIO_MODE_OUTPUT);//把这个GPIO作为输出	
	}
	
	return ESP_OK;
}









